# -*- coding:utf-8 -*-

from cn.pigersing.PyShare.Services.Chat import ChatClientBase
from cn.pigersing.PyShare.Services.FileShare import FileShareClientBase
from cn.pigersing.PyShare.Services.ServiceServer import ServerManager
from cn.pigersing.PyShare.Event import add_listener
from pprint import pprint
import cmd
import threading


class ConsoleFileShare(FileShareClientBase): 
    
    def __init__(self, buffer_size=1024 ** 2):
        
        FileShareClientBase.__init__(self, buffer_size)
        add_listener('FileShare.FileProgress', self.recv_progress)
    
    
    def handler_recv(self, evt):
        # file descriptor
        filrqst = evt.args[1]
        session = evt.args[2]
        
        dest_name, ret = self.save_where(filrqst, session)
        
        if ret :
            self.start_receive(filrqst, session, dest_name)
        else:
            self.deny_recv(filrqst, session)
        
        
    def save_where(self, file_request, session): 
        fname = file_request.file_name
        msg = 'peer:%s wants to send you a file:"%s", size:%d\nDo you agree?(Please input yes or no )' % (session.id, fname, file_request.file_size)
        ret = raw_input(msg)
        if ret.lower() in ('yes', 'y') :
            ret = True
            path = os.path.join(self.get_home_dir(), 'recv_' + file_request.file_name)
            msg = 'file is to be put at: %s\nSet a new directory or press "Enter" to continue' % (path,)
            u_path = raw_input(msg)
            if len(u_path) > 0 :
                if not os.path.exists(os.path.dirname(u_path)) :
                    print 'the directory you set is not valid, the default will be used'
                else :
                    print 'new path is:"%s"' % u_path
                    path = u_path
            
            return path, True
            
        else :
            print 'You have denied is request'
            
    
    def recv_progress(self, evt): 
        file_request, s, recved, delta = evt.args[1:5]
        ratio = (100.0 * recved) / file_request.file_size
        speed = (recved / 1024.0 / 1024.0) / delta
        print 'file:"%s" processed: %-6.2f%%(%-6.2fMB/s)' % (file_request.file_name, ratio, speed)
        
        
    def get_home_dir(self):
        ''' Try to find user's home directory, otherwise return current directory.'''
        try:
            path1 = os.path.expanduser("~")
        except:
            path1 = ""
        try:
            path2 = os.environ["HOME"]
        except:
            path2 = ""
        try:
            path3 = os.environ["USERPROFILE"]
        except:
            path3 = ""

        if not os.path.exists(path1):
            if not os.path.exists(path2):
                if not os.path.exists(path3):
                    return os.getcwd()
                else: return path3
            else: return path2
        else: return path1


class ConsoleChatClient(ChatClientBase):
    
    def handle_message(self, evt):
        # get message instance
        msg = evt.ret
        session = self.server.get_session(msg.sender)
        if session and session.id != self.server.id:
            fname = session.nickname
            if msg.to != '*' :
                tname = self.server.nickname
            else:
                tname = 'all'
                
            s = '[message from %s, to %s at %s]:%s' % (fname, tname, '%d:%d:%d' % msg.time_span[3:], msg.msg)
            print s
            

def _server_running_guard(meth):
    
    def _caller(*args, **dict_args):
        
        # get instance
        inst = args[0]
        if inst.running :
            return meth(*args, **dict_args)
        else :
            print 'Server is not running, please start a server first!'

    return _caller


class ConsoleApplication(cmd.Cmd):
    
    prompt = '>>'
    SEND_CMD = [ 'file', 'message' ]
    
    def __init__(self):
        
        cmd.Cmd.__init__(self)
        #
        self.running = False
        self.server = None
        #
        self.chat = ConsoleChatClient()
        self.file_share = ConsoleFileShare()
        
    
    def _parse_args(self, args):
        
        ret_args = []
        ret_dict = {}

        def _remove_quote(src):
            if src.startswith('"') and src.endswith('"') :
                return src[1: - 1]
            elif not (not src.startswith('"') and not src.endswith('"')) :
                raise Exception("argument invalid %s" % src)
            else :
                return src
        
        # split the coming argument
        args = args.split(',')
        for arg in args :
            # check if it is a key-value pair
            dic = arg.split('=')
            if len(dic) == 2 :
                ret_dict[dic[0].strip()] = _remove_quote(dic[1].strip())
            elif len(dic) > 2 or len(dic) == 0 :
                raise Exception('Bad argument format: %s' % arg)
            else :
                ret_args.append(_remove_quote(arg.strip()))
            
        return tuple(ret_args), ret_dict
        
    
    def do_exit(self, args):
        if self.running :
            self.do_shutdown(None)
        exit(1)
        
    
    def do_EOF(self, line):
        if self.running :
            self.do_shutdown(None)
        return True
    
    
    def do_start(self, args): 
        
        if self.running :
            print 'The server is running, if you want to start a new one, please stop the previous first!'
            return
        
        # parse arguments
        args, dic = self._parse_args(args)
        if len(args) > 0 :
            nickname = args[0]
        
        self.server = ServerManager()
        self.chat.set_server(self.server)
        self.file_share.set_server(self.server)
        # set nickname if assigned
        if nickname != '' :
            self.server.nickname = nickname
        # start a server_thread
        self.server.start_server()
        self.running = True
        #
        # notify the user
        print 'Server start successfully!'
        
    
    def do_isrunning(self, args):
        print self.running
    
    
    @_server_running_guard
    def do_shutdown(self, args): 
        
        print 'Stopping the server...'
        self.server.shutdown()
        # wait for server stopping
        self.running = False
        print 'Server stop successfully!'
    
    
    @_server_running_guard
    def do_list(self, args): 
        peer_list = [ '%s:%s' % (s.id, s.nickname) for s in self.server.to_list() if not s is self.server  ]
        pprint( peer_list )
    
    
    @_server_running_guard
    def do_send(self, args): 
        doc = '''Send a text message or a file to remote peer, use the following format
                        for text message: "id_to, message" or " to=id, message=something "
                        for file: "to=id, file=file path"
                '''
        
        # parse arguments
        args, dic = self._parse_args(args)
        
        if len(args) > 1 :
            # default send message in format "to, message"
            self._send_txtmsg(args[0], args[1])
        else :
            to = dic['to']
            
            if dic.has_key('message') :
                self._send_txtmsg(to, dic['message'])
            elif dic.has_key('file') :
                self.file_share.send_file(dic['file'], to)
            else:
                print doc
            
            
    def _send_txtmsg(self, to, msg):
        
        if to == '*':
            self.chat.send_to_all(msg)
        else:
            self.chat.send(msg, to)
    
    
    def complete_send(self, text, line, begidx, endidx): pass
    
    
#===============================================================================
#  test code here
#===============================================================================

if __name__ == '__main__' :
    
    ConsoleApplication().cmdloop()
